<template>
  <div :class="classes.editWrap" class="colorBgContainer_pubCol">
	<!-- 大纲 -->
    <!-- <a-affix :offset-top="80" style="position: absolute; left: 50px">
      <div :class="classes.leftBar">
        <DirBar v-if="true" :dir="dir" />
      </div>
    </a-affix> -->
    <div class="upload_orc_title">
		<div :class="classes.toolbar" class="colorBgContainer" :style="{ padding: paddingVal }">
			<div :class="classes.innerToolBar" :style="{ padding: innerToolBarPadding }">
				<div>
					<!-- <a-tooltip content="保存">
						<span :class="[classes.item, 'colorTextLabel']" @click="handleSave()">
							<icon-save />
						</span>
					</a-tooltip> -->
					<a-tooltip content="撤销">
						<span :class="[classes.item, undoState ? 'colorTextLabel' : 'colorFillContentHover']" @click="handleRecord('undo')">
							<icon-undo />
						</span>
					</a-tooltip>
					<a-tooltip content="重做">
						<span :class="[classes.item, undoState ? 'colorTextLabel' : 'colorFillContentHover']" @click="handleRecord('redo')">
							<icon-redo />
						</span>
					</a-tooltip>
					<a-divider direction="vertical" />
					<span v-for="(item, i) in toolbar.editorToolbarlist" :key="item.type" :class="toolBarClass(item.type)">
						<template v-for="ele in item.data" :key="ele.type">
							<a-popover :title="ele.text" position="tl" v-if="ele.type === 'H'">
								<template #content>
									<div :class="classes.hwrap">
										<span :class="classes.hitem" class="borderRadius" v-for="(v, index) in ele.options" :key="v" @click="handleToolbar(item.type, ele.type, index)">
											<a-typography-title :heading="getHeadingLevel(index)">{{ v }}</a-typography-title>
										</span>
									</div>
								</template>
								<span :class="[classes.item, classes.userSelect, 'colorTextLabel']">{{ ele.icon }}</span>
							</a-popover>
							<a-popover :title="ele.text" position="tl" v-else-if="ele.type === 'Emoji'">
								<template #content>
									<div :class="classes.hwrap" @mousedown="(e) => e.preventDefault()">
										<span :class="classes.mitem" class="borderRadius" v-for="v in ele.options" :key="v" @click="handleInsertEmogi(String(v))">
											<span>{{ v }}</span>
										</span>
									</div>
								</template>
								<span :class="[classes.item, classes.userSelect, 'colorTextLabel']" v-html="ele.html"> </span>
							</a-popover>
							<a-tooltip v-else-if="['F', 'LineHeight'].includes(ele.type)" :content="ele.text">
								<a-dropdown @select="handleFontChange($event, ele.type)" trigger="hover">
									<span v-if="ele.html" :class="[classes.item, classes.userSelect, 'colorTextLabel']" v-html="ele.html"> </span>
									<span v-else :class="[classes.item, classes.userSelect, 'colorTextLabel']">
										<component :is="ele.icon" :style="{ color: ele.iconCol }" />
									</span>
									<template #content>
										<a-doption v-for="item in ele.options" :key="item" :class="classes.userSelect">{{ item }}</a-doption>
									</template>
								</a-dropdown>
							</a-tooltip>
							<a-tooltip v-else :content="ele.text">
								<span v-if="ele.html" :class="[classes.item, 'colorTextLabel']" @click="handleToolbar(item.type, ele.type)" v-html="ele.html"> </span>
								<span v-else :class="[classes.item, 'colorTextLabel']" @click="handleToolbar(item.type, ele.type)">
									<component :is="ele.icon" :style="{ color: ele.iconCol }" />
								</span>
							</a-tooltip>
						</template>
						<a-divider v-if="i != toolbar.editorToolbarlist.length - 1" direction="vertical" />
					</span>
					<!-- <a-tooltip content="更多操作">
						<a-dropdown @select="handleMoreSelect" trigger="hover">
							<span :class="[classes.item, 'colorTextLabel']">
								<icon-more />
							</span>
							<template #content>
								<a-doption v-for="item in moreOptiosn" :key="item.key" :value="item.key" :class="classes.userSelect">{{ item.text }}</a-doption>
							</template>
						</a-dropdown>
					</a-tooltip> -->
				</div>
			</div>
		</div>
	</div>
      <a-empty style="position: absolute;top: 50%;z-index: 999;" v-if="!ocrData">
        <template #image>
          <icon-exclamation-circle-fill />
        </template>
        请上传文件
      </a-empty>
    
    <div :class="classes.editBox" :style="{ margin: '50px auto 0' }" ref="docRef" id="js_print_el">
      <WaterPanel :visible="waterShow" :value="waterConfig" @handleWaterChange="handleWaterChange" @handleWaterModalClose="handleWaterModalClose">
        <a-layout-content
          id="pageContent"
          style="margin: 0 120px; position: relative; overflow: hidden; padding: 20px; border-radius: 6px; min-height: 700px"
          class="colorBgContainer pubCol"
        >
          <div v-if="isLoading" :class="classes.isLoadingBox">
            <a-spin :loading="isLoading" tip="编辑器组件加载中...">
              <template #icon>
                <icon-sync />
              </template>
            </a-spin>
          </div>
          <!-- <div v-else :class="classes.titleIpt">
            <div :class="classes.titleArea">
              {{ title }}
              <div :class="classes.descArea" class="colorTextLabel">
                总字数：{{ curWords }}, 预计阅读 {{ formatUsedTime((curWords / 200) * 60) }}&nbsp;
                <span v-if="pageStatus.ct">发布时间:{{ formatDate(pageStatus.ct) }} </span>&nbsp;
                <span v-if="pageStatus.ut">更新时间:{{ formatDate(pageStatus.ut) }} </span>&nbsp;
                <span v-if="pageStatus.pv">访问量:{{ pageStatus.pv }} </span>&nbsp;
                <span v-if="pageStatus.pv">围观人数:{{ pageStatus.uv }} </span>
              </div>
            </div>
          </div> -->
          
          <EditorJs :minHeight="60" :readOnly="false" @getEditorInstance="getEditorInstance" @handleEditorChange="handleEditorChange" />
          <!-- <div style="text-align: center" v-if="!!pagation.total && pagation.total >= pagation.page">
            <a-button type="primary" @click="handleNextPage">当前为第{{ pagation.page }}页，共{{ pagation.total + 1 }}页</a-button>
          </div> -->
        </a-layout-content>
      </WaterPanel>
      <!-- <a-affix :offsetBottom="232" style="position: absolute; right: -20px" :class="classes.barWrap">
				<Bar :list="barList" @handleBarClick="handleBarClick"></Bar>
			</a-affix> -->
    </div>
  </div>
</template>

<script setup lang="ts">
import { getDoc, addDoc, clearDoc, saveVersion, getVersion } from "../../dataModel"
import toolbar, { type ToolItemType } from "./toolbarPre"
import { WatermarkConfig } from '@/composables/WaterPael';
import { formatDate, setHeadMeta, store, convertBooleans } from '@/utils';
import { computedWords, formatUsedTime } from '@/components/EditorJs/utils/computed';
import classes from './index.module.less';
import DirBar from '../DirBar/index.vue';
import Bar, { type ControlItem } from '../InteractionBar/Bar.vue';
// import { getDoc } from '../../dataModel';

const undoState = ref<boolean>(false);
const redoState = ref<boolean>(false);
const docRef = ref();
const waterShow = ref<boolean>(false);
const isLoading = ref<boolean>(true);
const schema = ref<any[]>([]);
const barList = ref<ControlItem[]>([]);
const dir = ref<any[]>([]);
// 文档标题
const title = ref<string>('');
// 水印配置项
const waterConfig = reactive<any>({ a: 111 });
const editorRef = ref();
const curWords = ref<number>(0);
// 页面puv等信息
const pageStatus = reactive<any>({});
const pagation = reactive({
  page: 1,
  total: 0
});

const clearCommonState = () => {
  // 清除页面已有状态
  Object.assign(waterConfig, {});
  barList.value = [];
};
const getEditorInstance = (editor: any) => {
  editorRef.value = editor;
  isLoading.value = false;
  clearCommonState();
  fetchPublicData();
};
const generateDir = (blocks: any[] = []) => {
  const headList = ['h2', 'h3', 'h4', 'h5'];
  return blocks
    ?.map((v: any) => {
      if (v.type === 'header' && v.data.level > 1) {
        return {
          id: v.id,
          tag: headList[v.data.level - 2],
          text: v.data.text
        };
      }
      return false;
    })
    .filter(Boolean) as { id: string; tag: string; text: string }[];
};
const handleEditorChange = async (api: any, event: any) => {
  // @ts-ignore
  // 更新当前聚焦的位置
  curFocusRef.value = event.detail ? event.detail.index : -1
  // 生成目录
  try {
  	const data = await api.saver.save()
  	const headData = generateDir(data.blocks)
  	dir.value = headData
  } catch (err) {}
  redoState.value = true
  undoState.value = true
};
// 水印面板配置变化
const handleWaterChange = (data: WatermarkConfig) => {
  Object.assign(waterConfig, data);
};
const handleWaterModalClose = () => {
  waterShow.value = false;
};

const updateDocData = (newData: any, isOver?: boolean) => {
  title.value = newData.title;
  const newSchema = isOver ? [...newData.schema] : [...schema.value, ...newData.schema];
  editorRef.value.blocks.render({
    blocks: newSchema
  });

  schema.value = newSchema;

  const headData = generateDir(newSchema);
  dir.value = headData;
  curWords.value = computedWords(newSchema);

  barList.value = newData.config?.bar || [];
  Object.assign(waterConfig, newData.config?.waterConfig || {});
};
const fetchPublicData = async (page: number = 1, cb?: () => void) => {
  // const res: any = getDoc();
  const res: any = null;
  // 检查是否有分页
  const pageRows: number[] = [];
  // const schema = res.schema ;
  const schema = res ? res.schema : [] ; // wjy
  let curSchema: any;
  schema.forEach((v: any, i: number) => {
    if (v.type === 'pageLine') {
      pageRows.push(i);
    }
  });
  if (pageRows.length) {
    // 如果是第一次获取
    if (page == 1) {
      curSchema = schema.slice(0, pageRows[0]);
    } else {
      curSchema = schema.slice(pageRows[page - 2] + 1, pageRows[page - 1]);
    }
  }

  const newData = { ...res, schema: curSchema || schema, total: pageRows.length, page };

  // 更新数据
  updateDocData(newData, page < 2)
  // 更新header
  setHeadMeta({
    title: res ? res.title : '',
    desc: ''
  });

  // 设置分页信息
  if (pageRows.length) {
    pagation.page = page;
    pagation.total = pageRows.length;
  } else {
    pagation.page = 1;
    pagation.total = 0;
  }
  Object.assign(pageStatus, {
    uv: 6999,
    pv: 9999,
    ct: 1722441994180
  });

  cb && cb();
};
const handleBarClick = (type: ControlItem['type'] | string) => {
  barList.value = barList.value.map((v: any) => {
    if (v.type === 'like') {
      return {
        ...v,
        config: {
          flovers: (v.config?.flovers || 0) + 1
        }
      };
    }
    return v;
  });
};
const handleNextPage = () => {
  fetchPublicData(pagation.page + 1);
};
///////////////////////////////////////////// 自定义开发 2025-01-07
import { mdRender } from '@/components/tMd/mdRender';
import { Message } from "@arco-design/web-vue"
const ocrData = ref<any>('');
const upDataOcr = (data: any) => {
	// 更新数据 data为ocr识别的结果 这里只是md格式 还有json格式
	// 这里需要将md格式转换成editorjs格式
  ocrData.value = data;
  const editorData = mdRender(data);
  console.log(editorData);
  
  editorRef.value.blocks.render({
    blocks: editorData.filter((value) => Object.keys(value).length !== 0) // filter through array and remove empty objects
  });
};
// 下载
const downloadFile = async  () => {
	// console.log(await editorRef.value.saver.save());
	await editorRef.value.saver.save()
	return;
	// editorRef.value.save()
  if (!ocrData.value) {
    Message.error('请先识别文档内容');
    return;
  }
  editorRef.value.blocks.insert("markdownParser", {})
  return;
  // 原生下载不支持只读模式下载 所以换种方式下载
  // editorRef.value.blocks.insert("markdownParser", {})
  const blob = new Blob([ocrData.value], {type: 'text/markdown'});
  // 创建一个临时的下载链接元素
  const link = document.createElement('a');
  link.href = URL.createObjectURL(blob);
  // 设置下载的文件名
  link.download = '导出.md';
  // 将链接添加到文档中（这一步不是必须的，但在某些情况下可以确保兼容性）
  document.body.appendChild(link);
  // 触发点击事件开始下载
  link.click();
  // 下载完成后，移除链接元素并释放URL对象
  document.body.removeChild(link);
  URL.revokeObjectURL(link.href);
}
////////////////////////////////
const paddingVal = computed(() => `${16}px ${20 * 2.5}px`)
const innerToolBarPadding = computed(() => `0 ${20}px`)
// 保存
const handleSave = async () => {
	const data = await editorRef.value.save()
	const doc = {
		title: title.value,
		schema: data.blocks,
		config: {
			bar: barList.value,
			waterConfig: waterConfig,
		},
	}
	// addDoc(doc)
	// saveVersion(doc)
	Message.success("保存成功")
}
const handleRecord = (type: "undo" | "redo") => {
	editorRef.value.record[type]()
}
const toolBarClass = (type: string) => {
	return ["base", "media"].includes(type) ? classes.controlWrap : classes.operationWrap
}
const getHeadingLevel = (index: number): 1 | 2 | 3 | 4 | 5 | 6 => {
	return (index + 1) as 1 | 2 | 3 | 4 | 5 | 6
}
// 工具栏事件
const handleToolbar = (cate: string, type: string, idx?: number) => {
	if (cate === "base") {
		switch (type) {
			case "H":
				insertBlock("header", {
					level: (idx || 0) + 1,
					text: "标题",
				})
				break
			case "AiWrite":
				insertBlock("aiWrite", {
					text: "",
				})
				break
			case "Link":
				insertBlock("link", {
					link: "",
					meta: {},
				})
				break
			case "Image":
				insertBlock("image", {
					file: {
						// "url" : ""
					},
					caption: "",
					withBorder: false,
					withBackground: false,
					stretched: false,
				})
				break
			case "Quote":
				insertBlock("quote", {
					text: "",
				})
				break
			case "Table":
				insertBlock("table", {
					withHeadings: true,
					content: [],
				})
				break
			case "Checkbox":
				insertBlock("checklist", {
					items: [
						{
							text: "任务一",
							checked: false,
						},
						{
							text: "任务二",
							checked: false,
						},
					],
				})
				break
			case "UnorderList":
			case "OrderList":
				insertBlock("list", {
					style: type !== "OrderList" ? "unordered" : "ordered",
					items: [
						{
							content: "任务一",
							items: [
								{
									content: "锻炼",
									items: [],
								},
							],
						},
						{
							content: "任务二",
						},
					],
				})
				break
			case "CodeBox":
				insertBlock("codeBox", {
					code: "",
				})
				break
			case "Divider":
				insertBlock("divider", {
					color: "",
				})
				break
			case "Alert":
				insertBlock("alert", {
					type: "",
					align: "left",
					text: "",
				})
				break
		}
	}
	if (cate === "media") {
		switch (type) {
			case "Math":
				insertBlock("math", {
					value: "",
				})
				break
			case "Audio":
				insertBlock("audio", {
					url: "",
				})
				break
			case "Video":
				insertBlock("video", {
					url: "",
				})
				break
			case "Iframe":
				insertBlock("iframe", {
					url: "",
				})
				break
			case "PDFParser":
				insertBlock("PDFParser", {
					url: "",
				})
				break
			case "DocParser":
				insertBlock("DocParser", {
					text: "",
				})
				break
			case "PDFTextParser":
				insertBlock("PDFTextParser", {
					text: "",
				})
				break
			case "bar":
			case "line":
			case "pie":
				insertBlock("chart", {
					type,
				})
				break
			case "MindChart":
				insertBlock("mind", {})
				break
		}
		return
	}
	if (cate === "operate") {
		switch (type) {
			case "Clear":
				editorRef.value.clear()
				clearDoc()
				break
			case "UploadJson":
				const fileDom = make("input", "", { type: "file" })
				fileDom.style.display = "none"
				document.body.append(fileDom)
				fileDom.click()
				fileDom.addEventListener("change", (event: any) => {
					// 处理文件选择事件
					const file = event.target.files[0]
					const reader: any = new FileReader()
					reader.readAsText(file)

					reader.onload = () => {
						try {
							let json = JSON.parse(reader.result)
							editorRef.value.blocks.render({
								blocks: json.blocks,
							})
						} catch (err) {
							const editorData = mdRender(reader.result)
							editorRef.value.blocks.render({
								blocks: editorData.filter((value) => Object.keys(value).length !== 0), // filter through array and remove empty objects
							})
						}
					}
				})
				break
			case "Download":
				showDownload.value = true
				break
			case "Readonly":
				location.href = "/docx-vue/preview"
				break
			case "Water":
				waterShow.value = true
				break
		}
		return
	}
}
const curFocusRef = ref<number>(-1)
// 插入元素控件
const insertBlock = (type: string, data: any, config: any = {}) => {
	const ideaIdx = editorRef.value.blocks.getCurrentBlockIndex()
	let curIdx = ideaIdx > -1 ? ideaIdx : curFocusRef.value
	const curBlock = editorRef.value.blocks.getBlockByIndex(curIdx)
	if (curBlock.isEmpty) {
		editorRef.value.blocks.delete(curIdx)
		curIdx = curIdx - 1
	}
	const nextIdx = curIdx + 1
	editorRef.value.blocks.insert(type, data, config, nextIdx, true)
	curFocusRef.value = nextIdx
}
// 处理插入自定义表情
const handleInsertEmogi = (emoji: string) => {
	if (!editorRef.value) {
		console.error("编辑器还为初始化")
		return
	}
	const selection: any = window.getSelection()
	if (!selection.rangeCount) return

	const range = selection.getRangeAt(0)
	const textNode = document.createTextNode(emoji)
	range.deleteContents()
	range.insertNode(textNode)

	// 在插入表情之后移动鼠标光标
	range.setStartAfter(textNode)
	range.setEndAfter(textNode)
	selection.removeAllRanges()
	selection.addRange(range)
	editorRef.value.save()
}
// ----- 字体选择逻辑, 实现跨editor容器通信 start ----------
const handleFontChange = (font: string | number | Record<string, any> | undefined, type: string) => {
	switch (type) {
		case "F":
			if (editorRef.value) {
				const selection = window.getSelection()
				if (selection && !selection.isCollapsed) {
					applyFontToSelection(selection, font as string)
					editorRef.value.save()
				}
			}
			break
		case "LineHeight":
			handleLineHeightChange(font as string)
			break
	}
}
const applyFontToSelection = (selection: Selection, font: string) => {
	const range = selection.getRangeAt(0)
	const fragment = range.extractContents()
	const span = document.createElement("span")
	span.style.fontFamily = font

	// Recursively apply font to all child nodes
	const applyFontRecursively = (node: Node) => {
		if (node.nodeType === Node.TEXT_NODE) {
			const newSpan = span.cloneNode() as HTMLSpanElement
			newSpan.textContent = node.textContent
			return newSpan
		} else if (node.nodeType === Node.ELEMENT_NODE) {
			const element = node as HTMLElement
			const newElement = element.cloneNode(false) as HTMLElement
			newElement.style.fontFamily = font
			for (let child of Array.from(element.childNodes)) {
				newElement.appendChild(applyFontRecursively(child))
			}
			return newElement
		}
		return node.cloneNode(true)
	}

	const newFragment = document.createDocumentFragment()
	for (let child of Array.from(fragment.childNodes)) {
		newFragment.appendChild(applyFontRecursively(child))
	}

	span.appendChild(newFragment)
	range.insertNode(span)
	selection.removeAllRanges()
	selection.addRange(range)
}
const handleLineHeightChange = (val: string) => {
	const value = val
	if (editorRef.value) {
		const currentBlockIndex = editorRef.value.blocks.getCurrentBlockIndex()

		if (currentBlockIndex !== undefined) {
			const block = editorRef.value.blocks.getBlockByIndex(currentBlockIndex)
			if (block && block.name === "paragraph") {
				const dom = document.querySelector(`[data-id="${block.id}"]`) as HTMLElement
				if (dom.querySelector(".line-wrap")) {
					const lineWrapElement = dom.querySelector(".line-wrap")
					if (lineWrapElement) {
						;(lineWrapElement as HTMLElement).style.lineHeight = value
					}
				} else {
					const lineWrap = document.createElement("div")
					lineWrap.classList.add("line-wrap")
					lineWrap.style.lineHeight = value
					const cdxBlock = dom.querySelector(".cdx-block")
					if (cdxBlock) {
						lineWrap.innerHTML = cdxBlock.innerHTML
						cdxBlock.innerHTML = ""
						cdxBlock.appendChild(lineWrap)
					}
				}

				editorRef.value.save()
			}
		}
	}
}
const moreOptiosn = ref<any[]>([
	{
		key: "version",
		text: "版本管理",
	},
]) // 更多操作
const versionVis = ref<boolean>(false) // 版本管理弹窗
const versionList = ref<any[]>([]) // 版本列表

// 更多操作
const handleMoreSelect = (val: any) => {
	if (val === "version") {
		versionVis.value = true
		versionList.value = getVersion()
	}
}
defineExpose({
  upDataOcr,
  downloadFile,
  editorRef
});
</script>

<style scoped>
:deep(.arco-spin-tip) {
  font-size: 12px !important;
  margin-top: 10px !important;
  color: var(--color-neutral-10);
}
:deep(.arco-input-wrapper) {
  padding: 0;
  border: none;
  background: var(--color-bg-1);
}
:deep(.arco-input-wrapper .arco-input) {
  background: var(--color-bg-1);
}
.blank-space {
  background: #fff;
}
.colorBgContainer_pubCol {
  position: relative;
  overflow: auto;
  flex: 1;
}
.upload_orc_title{
  font-size: 14px;
  color: #666;
  background-color: white;
  height: 60px;
  display: flex;
  justify-content: center;
  align-items: center;
}
._editBox_35djg_59{
  height: calc(100% - 140px);
  overflow: auto;
  width: 100%;
}
._editBox_35djg_59 > div{
	height: 100%;
}
:deep(.arco-watermark){
	height: 100%;
}
:deep(._toolbar_35djg_1.colorBgContainer){
	width: auto;
	box-shadow: none;
	z-index: auto;
}
:deep(.arco-layout-content.colorBgContainer.pubCol){
	margin:0 50px !important;
	height: 100%;
}
</style>

